Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Commit 81b574da2935ed97421a6257f74def87d77c46e7


Parents : 4febae8
Author : Sudo-Ivan <ivan@quad4.io>
Signature : Signature validation error
Date : 2026-01-16T08:53:00-06:00

Refactor notification handling and improve error logging

- Changed parameter name from `type` to `notification_type` in the `add_notification` method for clarity.
- Improved error handling in various modules by using `contextlib.suppress` to gracefully handle exceptions without cluttering the code with try-except blocks.
- Improved logging for bot state saving failures to aid in debugging.

Changes
Diff

diff --git a/meshchatx/meshchat.py b/meshchatx/meshchat.py
index 40648ab4..26e93677 100644
--- a/meshchatx/meshchat.py
+++ b/meshchatx/meshchat.py
@@ -1870,7 +1870,7 @@ class ReticulumMeshChat:
return
# Add system notification
self.database.misc.add_notification(
- type="telephone_voicemail",
+ notification_type="telephone_voicemail",
remote_hash=remote_hash,
title="New Voicemail",
content=f"New voicemail from {remote_name or remote_hash} ({duration}s)",
@@ -2021,7 +2021,7 @@ class ReticulumMeshChat:
# Actually, persistent notification is good.
self.database.misc.add_notification(
- type="telephone_missed_call",
+ notification_type="telephone_missed_call",
remote_hash=remote_identity_hash,
title="Missed Call",
content=f"You missed a call from {remote_identity_name or remote_identity_hash}",

diff --git a/meshchatx/src/backend/bot_handler.py b/meshchatx/src/backend/bot_handler.py
index ee1baf0c..755a4d54 100644
--- a/meshchatx/src/backend/bot_handler.py
+++ b/meshchatx/src/backend/bot_handler.py
@@ -1,3 +1,4 @@
+import contextlib
import json
import logging
import os
@@ -44,8 +45,8 @@ class BotHandler:
try:
with open(self.state_file, "w", encoding="utf-8") as f:
json.dump(self.bots_state, f, indent=2)
- except Exception:
- pass
+ except Exception as exc:
+ logger.error("Failed to save bots state: %s", exc)
def get_available_templates(self):
return [
@@ -104,22 +105,18 @@ class BotHandler:
and getattr(instance, "bot", None)
and getattr(instance.bot, "local", None)
):
- try:
+ with contextlib.suppress(Exception):
address_pretty = RNS.prettyhexrep(instance.bot.local.hash)
address_full = RNS.hexrep(instance.bot.local.hash, delimit=False)
- except Exception:
- pass
# Fallback to identity file on disk
if address_full is None:
identity = self._load_identity_for_bot(bot_id)
if identity:
- try:
+ with contextlib.suppress(Exception):
destination = RNS.Destination(identity, "lxmf", "delivery")
address_full = destination.hash.hex()
address_pretty = RNS.prettyhexrep(destination.hash)
- except Exception:
- pass
bots.append(
{
@@ -209,8 +206,10 @@ class BotHandler:
if pid:
try:
if sys.platform.startswith("win"):
- subprocess.run(
- ["taskkill", "/PID", str(pid), "/T", "/F"],
+ # Use absolute path if possible to avoid S607
+ taskkill = shutil.which("taskkill") or "taskkill"
+ subprocess.run( # noqa: S603
+ [taskkill, "/PID", str(pid), "/T", "/F"],
check=False,
timeout=5,
)

diff --git a/meshchatx/src/backend/bot_process.py b/meshchatx/src/backend/bot_process.py
index 91f53ed1..047c48f0 100644
--- a/meshchatx/src/backend/bot_process.py
+++ b/meshchatx/src/backend/bot_process.py
@@ -1,4 +1,5 @@
import argparse
+import contextlib
import os
from meshchatx.src.backend.bot_templates import (
@@ -30,13 +31,11 @@ def main():
bot_instance = BotCls(name=args.name, storage_path=args.storage, test_mode=False)
# Optional immediate announce for reachability
- try:
+ with contextlib.suppress(Exception):
if hasattr(bot_instance.bot, "announce_enabled"):
bot_instance.bot.announce_enabled = True
if hasattr(bot_instance.bot, "_announce"):
bot_instance.bot._announce()
- except Exception:
- pass
bot_instance.run()

diff --git a/meshchatx/src/backend/database/contacts.py b/meshchatx/src/backend/database/contacts.py
index af616e4f..5f07b74f 100644
--- a/meshchatx/src/backend/database/contacts.py
+++ b/meshchatx/src/backend/database/contacts.py
@@ -110,7 +110,7 @@ class ContactsDAO:
return
updates.append("updated_at = CURRENT_TIMESTAMP")
- query = f"UPDATE contacts SET {', '.join(updates)} WHERE id = ?"
+ query = f"UPDATE contacts SET {', '.join(updates)} WHERE id = ?" # noqa: S608
params.append(contact_id)
self.provider.execute(query, tuple(params))

diff --git a/meshchatx/src/backend/database/messages.py b/meshchatx/src/backend/database/messages.py
index 618d5322..e89f06d9 100644
--- a/meshchatx/src/backend/database/messages.py
+++ b/meshchatx/src/backend/database/messages.py
@@ -68,7 +68,7 @@ class MessageDAO:
return
placeholders = ", ".join(["?"] * len(message_hashes))
self.provider.execute(
- f"DELETE FROM lxmf_messages WHERE hash IN ({placeholders})",
+ f"DELETE FROM lxmf_messages WHERE hash IN ({placeholders})", # noqa: S608
tuple(message_hashes),
)

diff --git a/meshchatx/src/backend/database/misc.py b/meshchatx/src/backend/database/misc.py
index 9bc603b1..0b27b957 100644
--- a/meshchatx/src/backend/database/misc.py
+++ b/meshchatx/src/backend/database/misc.py
@@ -258,12 +258,12 @@ class MiscDAO:
)
# Notifications
- def add_notification(self, type, remote_hash, title, content):
+ def add_notification(self, notification_type, remote_hash, title, content):
now = datetime.now(UTC)
timestamp = datetime.now(UTC).timestamp()
self.provider.execute(
"INSERT INTO notifications (type, remote_hash, title, content, timestamp, created_at) VALUES (?, ?, ?, ?, ?, ?)",
- (type, remote_hash, title, content, timestamp, now),
+ (notification_type, remote_hash, title, content, timestamp, now),
)
def get_notifications(self, filter_unread=False, limit=50):

diff --git a/meshchatx/src/backend/identity_context.py b/meshchatx/src/backend/identity_context.py
index 63a0010f..58c2c077 100644
--- a/meshchatx/src/backend/identity_context.py
+++ b/meshchatx/src/backend/identity_context.py
@@ -1,4 +1,5 @@
import asyncio
+import contextlib
import os
import threading
@@ -212,14 +213,12 @@ class IdentityContext:
)
# Restore preferred propagation node on startup
- try:
+ with contextlib.suppress(Exception):
preferred_node = (
self.config.lxmf_preferred_propagation_node_destination_hash.get()
)
if preferred_node:
self.app.set_active_propagation_node(preferred_node, context=self)
- except Exception:
- pass
# 5. Initialize Handlers and Managers
self.rncp_handler = RNCPHandler(
@@ -458,10 +457,8 @@ class IdentityContext:
# 1. Deregister announce handlers
for handler in self.announce_handlers:
- try:
+ with contextlib.suppress(Exception):
RNS.Transport.deregister_announce_handler(handler)
- except Exception:
- pass
self.announce_handlers = []
# 2. Cleanup RNS destinations and links
@@ -469,10 +466,8 @@ class IdentityContext:
if self.message_router:
# Break cycles in mocks/objects
if hasattr(self.message_router, "register_delivery_callback"):
- try:
+ with contextlib.suppress(Exception):
self.message_router.register_delivery_callback(None)
- except Exception:
- pass
if hasattr(self.message_router, "delivery_destinations"):
for dest_hash in list(
@@ -534,11 +529,9 @@ class IdentityContext:
self.telephone_manager = None
if self.voicemail_manager:
- try:
+ with contextlib.suppress(Exception):
self.voicemail_manager.on_new_voicemail_callback = None
self.voicemail_manager.get_name_for_identity_hash = None
- except Exception:
- pass
self.voicemail_manager = None
if self.message_handler:

diff --git a/meshchatx/src/backend/identity_manager.py b/meshchatx/src/backend/identity_manager.py
index 328e1497..d6fe088c 100644
--- a/meshchatx/src/backend/identity_manager.py
+++ b/meshchatx/src/backend/identity_manager.py
@@ -1,4 +1,5 @@
import base64
+import contextlib
import json
import os
import shutil
@@ -49,11 +50,9 @@ class IdentityManager:
metadata_path = os.path.join(identity_path, "metadata.json")
metadata = None
if os.path.exists(metadata_path):
- try:
+ with contextlib.suppress(Exception):
with open(metadata_path) as f:
metadata = json.load(f)
- except Exception:
- pass
if metadata:
identities.append(
@@ -187,11 +186,9 @@ class IdentityManager:
# Merge with existing metadata if it exists
existing_metadata = {}
if os.path.exists(metadata_path):
- try:
+ with contextlib.suppress(Exception):
with open(metadata_path) as f:
existing_metadata = json.load(f)
- except Exception:
- pass
existing_metadata.update(metadata)

diff --git a/meshchatx/src/backend/meshchat_utils.py b/meshchatx/src/backend/meshchat_utils.py
index 87d549b4..6450a696 100644
--- a/meshchatx/src/backend/meshchat_utils.py
+++ b/meshchatx/src/backend/meshchat_utils.py
@@ -1,4 +1,5 @@
import base64
+import contextlib
import json
import signal
import threading
@@ -50,6 +51,8 @@ def message_fields_have_attachments(fields_json: str | None):
fields = json.loads(fields_json)
except Exception:
return False
+ if not isinstance(fields, dict):
+ return False
if "image" in fields or "audio" in fields:
return True
if "file_attachments" in fields and isinstance(
@@ -128,31 +131,25 @@ def parse_lxmf_display_name(
else:
app_data_bytes = base64.b64decode(app_data_base64)
- # Try using the library first
- try:
- display_name = LXMF.display_name_from_app_data(app_data_bytes)
- if display_name is not None:
- return display_name
- except (AttributeError, Exception):
- # Handle cases where library might fail or has the 'str' object has no attribute 'decode' bug
- pass
-
- # Fallback manual parsing if library failed or returned None
+ # Try manual parsing first to avoid LXMF library call.
if len(app_data_bytes) > 0:
- # Version 0.5.0+ announce format (msgpack list)
if (
app_data_bytes[0] >= 0x90 and app_data_bytes[0] <= 0x9F
) or app_data_bytes[0] == 0xDC:
- try:
+ with contextlib.suppress(Exception):
peer_data = msgpack.unpackb(app_data_bytes)
if isinstance(peer_data, list) and len(peer_data) >= 1:
dn = peer_data[0]
if dn is not None:
if isinstance(dn, bytes):
- return dn.decode("utf-8")
+ return dn.decode("utf-8", errors="replace")
return str(dn)
- except Exception:
- pass
+
+ # If manual parsing didn't work, try using the library as a fallback.
+ with contextlib.suppress(AttributeError, Exception):
+ display_name = LXMF.display_name_from_app_data(app_data_bytes)
+ if display_name is not None:
+ return display_name
except Exception as e:
print(f"Failed to parse LXMF display name: {e}")
@@ -188,7 +185,7 @@ def parse_nomadnetwork_node_display_name(
else:
app_data_bytes = base64.b64decode(app_data_base64)
- return app_data_bytes.decode("utf-8")
+ return app_data_bytes.decode("utf-8", errors="replace")
except Exception as e:
print(f"Failed to parse NomadNetwork display name: {e}")
return default_value

diff --git a/meshchatx/src/backend/recovery/crash_recovery.py b/meshchatx/src/backend/recovery/crash_recovery.py
index 87dc9b09..7c00118d 100644
--- a/meshchatx/src/backend/recovery/crash_recovery.py
+++ b/meshchatx/src/backend/recovery/crash_recovery.py
@@ -5,6 +5,7 @@ It utilizes Active Inference heuristics, Shannon Entropy, and KL-Divergence
to map application failures onto deterministic manifold constraints.
"""
+import contextlib
import os
import platform
import re
@@ -327,16 +328,16 @@ class CrashRecovery:
potential_causes["CONFIG_MISSING"]["probability"] = 0.99
# Filter and sort by probability
- for key, data in potential_causes.items():
- if data["probability"] > 0.3:
- causes.append(
- {
- "probability": int(data["probability"] * 100),
- "description": data["description"],
- "reasoning": data["reasoning"],
- "suggestions": data["suggestions"],
- },
- )
+ causes = [
+ {
+ "probability": int(data["probability"] * 100),
+ "description": data["description"],
+ "reasoning": data["reasoning"],
+ "suggestions": data["suggestions"],
+ }
+ for data in potential_causes.values()
+ if data["probability"] > 0.3
+ ]
causes.sort(key=lambda x: x["probability"], reverse=True)
@@ -450,7 +451,7 @@ class CrashRecovery:
file.write(f"- Python: {sys.version.split()[0]}\n")
# Resource Monitoring
- try:
+ with contextlib.suppress(Exception):
mem = psutil.virtual_memory()
results["available_mem_mb"] = mem.available / (1024**2)
file.write(
@@ -459,8 +460,6 @@ class CrashRecovery:
if mem.percent > 95:
results["low_memory"] = True
file.write(" [CRITICAL] System memory is dangerously low!\n")
- except Exception:
- pass
# Filesystem Status
if self.storage_dir:
@@ -475,7 +474,7 @@ class CrashRecovery:
" [ERROR] Storage path is NOT writable. Check filesystem permissions.\n",
)
- try:
+ with contextlib.suppress(Exception):
usage = shutil.disk_usage(self.storage_dir)
free_mb = usage.free / (1024**2)
file.write(f" - Disk Space: {free_mb:.1f} MB free\n")
@@ -483,8 +482,6 @@ class CrashRecovery:
file.write(
" [CRITICAL] Disk space is critically low (< 50MB)!\n",
)
- except Exception:
- pass
# Database Integrity
if self.database_path:
@@ -610,7 +607,7 @@ class CrashRecovery:
file.write(" - Logs: No RNS log files found in standard locations.\n")
# Check for interfaces and transport status
- try:
+ with contextlib.suppress(Exception):
# Try to get more info from RNS if it's already running
if hasattr(RNS.Transport, "interfaces") and RNS.Transport.interfaces:
results["active_interfaces"] = len(RNS.Transport.interfaces)
@@ -622,19 +619,15 @@ class CrashRecovery:
file.write(
" - Active Interfaces: None registered (Reticulum may not be initialized yet)\n",
)
- except Exception:
- pass
# Check for common port conflicts
common_ports = [4242, 8000, 8080] # Reticulum default is often 4242
for port in common_ports:
- try:
+ with contextlib.suppress(Exception):
for conn in psutil.net_connections():
if conn.laddr.port == port and conn.status == "LISTEN":
file.write(
f" [ALERT] Port {port} is already in use by PID {conn.pid}. Potential conflict.\n",
)
- except Exception:
- pass
return results

diff --git a/meshchatx/src/backend/rnstatus_handler.py b/meshchatx/src/backend/rnstatus_handler.py
index 783d3d4b..716b546b 100644
--- a/meshchatx/src/backend/rnstatus_handler.py
+++ b/meshchatx/src/backend/rnstatus_handler.py
@@ -1,3 +1,4 @@
+import contextlib
import time
from typing import Any
@@ -58,15 +59,13 @@ class RNStatusHandler:
blackhole_enabled = False
blackhole_sources = []
blackhole_count = 0
- try:
+ with contextlib.suppress(Exception):
blackhole_enabled = RNS.Reticulum.publish_blackhole_enabled()
blackhole_sources = [s.hex() for s in RNS.Reticulum.blackhole_sources()]
# Get count of blackholed identities
if self.reticulum and hasattr(self.reticulum, "get_blackholed_identities"):
blackhole_count = len(self.reticulum.get_blackholed_identities())
- except Exception:
- pass
interfaces = stats.get("interfaces", [])

diff --git a/meshchatx/src/backend/telephone_manager.py b/meshchatx/src/backend/telephone_manager.py
index 942e4e8c..a67358e0 100644
--- a/meshchatx/src/backend/telephone_manager.py
+++ b/meshchatx/src/backend/telephone_manager.py
@@ -1,5 +1,6 @@
import asyncio
import base64
+import contextlib
import os
import time
@@ -252,12 +253,10 @@ class TelephoneManager:
# Try reconstructing from public key
if announce.get("identity_public_key"):
- try:
+ with contextlib.suppress(Exception):
return RNS.Identity.from_bytes(
base64.b64decode(announce["identity_public_key"]),
)
- except Exception:
- pass
return None

diff --git a/meshchatx/src/backend/voicemail_manager.py b/meshchatx/src/backend/voicemail_manager.py
index 1dcc0bb6..eb900658 100644
--- a/meshchatx/src/backend/voicemail_manager.py
+++ b/meshchatx/src/backend/voicemail_manager.py
@@ -1,3 +1,4 @@
+import contextlib
import os
import platform
import shutil
@@ -300,10 +301,8 @@ class VoicemailManager:
# Stop microphone if it's active to prevent local noise being sent or recorded
if telephone.audio_input:
- try:
+ with contextlib.suppress(Exception):
telephone.audio_input.stop()
- except Exception:
- pass
# Play greeting
greeting_path = os.path.join(self.greetings_dir, "greeting.opus")
@@ -325,11 +324,9 @@ class VoicemailManager:
def session_job():
prev_receive_muted = self.telephone_manager.receive_muted
- try:
+ with contextlib.suppress(Exception):
# Prevent remote audio from playing locally while recording voicemail
self.telephone_manager.mute_receive()
- except Exception:
- pass
try:
# Wait for link to stabilize
@@ -417,11 +414,9 @@ class VoicemailManager:
if self.is_recording:
self.stop_recording()
finally:
- try:
+ with contextlib.suppress(Exception):
if not prev_receive_muted:
self.telephone_manager.unmute_receive()
- except Exception:
- pass
threading.Thread(target=session_job, daemon=True).start()

diff --git a/meshchatx/src/backend/web_audio_bridge.py b/meshchatx/src/backend/web_audio_bridge.py
index 2cbf0f3e..bf6584e1 100644
--- a/meshchatx/src/backend/web_audio_bridge.py
+++ b/meshchatx/src/backend/web_audio_bridge.py
@@ -1,4 +1,5 @@
import asyncio
+import contextlib
import json
import threading
@@ -218,12 +219,10 @@ class WebAudioBridge:
tele = self._tele()
if not tele:
return
- try:
- if hasattr(tele, "_Telephony__reconfigure_transmit_pipeline"):
- tele._Telephony__reconfigure_transmit_pipeline()
- except Exception:
- pass
- try:
+ with contextlib.suppress(Exception):
+ with contextlib.suppress(Exception):
+ if hasattr(tele, "_Telephony__reconfigure_transmit_pipeline"):
+ tele._Telephony__reconfigure_transmit_pipeline()
if tele.receive_pipeline:
tele.receive_pipeline.stop()
if tele.audio_output and self.rx_tee:
@@ -238,8 +237,6 @@ class WebAudioBridge:
sink=tele.audio_output,
)
tele.receive_pipeline.start()
- except Exception:
- pass
self.tx_source = None
self.rx_sink = None
self.rx_tee = None


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────